using System;
using System.IO;
using System.Xml;
using System.Collections;
using cuberga.Workflow;
using cuberga.Utils;
using cuberga.TSP; // ToDo by Lum 06-06-2007: Delete this dependency between GA & Workflow
//using cuberga.GA;
//using cuberga.ConcreteGADomain;
namespace cuberga.Utils
{
    class WorkflowLoader {

        private Hashtable _processes = new Hashtable();
        private Schema _schema;
        private string _srcFileName;
        private ICoreFactory _coreFactory;

        public WorkflowLoader(string srcFileName, ICoreFactory coreFactory) {
            _srcFileName = srcFileName;
            _coreFactory = coreFactory;
        }

        public Schema LoadFromFile()
        {
            _schema = new Schema();

            XmlDocument doc = new XmlDocument();
            doc.Load(_srcFileName);
            Parse(doc.ChildNodes[0]);

            return _schema;
        }

        private void MarkAsTerminationPoint(Process process, string terminatingFlag)
        {
            switch (terminatingFlag)
            {
                case "StartPoint":
                    _schema.MarkAsStartPoint(process);
                    break;
                case "FinishPoint":
                    _schema.MarkAsFinishPoint(process);
                    break;
                default:
                    // nothing for mid-point
                    break;
                
            }
        }
        private void ProcessNode(int id, string className, string terminatingFlag, object[] constructorParameters)
        {
            Console.WriteLine(id + ":" + className); // debug
            Process newProcess;

            if (className == "AndTripleInput" || 
                className == "AndOutput")
            {
                newProcess = new AndOutput(); // ToDo by Lum 06-06-2007: divide it at subnode.Name level
            }
            else
            {
                ICoreProcess coreProcess = _coreFactory.CreateCoreProcess(className, constructorParameters);
                newProcess = new Process(coreProcess);

            }
            //Process newProcess = GAFactory.Instance.CreateGAProcess(className, constructorParameters);
            //newProcess.Attach(LogObserver.Instance);
            _processes.Add(id, newProcess);  // we'll need it for links
            _schema.AddProcess(newProcess);

            MarkAsTerminationPoint(newProcess, terminatingFlag);
        }

        private void ProcessCondition(string className)
        {
            Console.WriteLine("??? " + className);

            ICoreProcess coreProcess = _coreFactory.CreateCoreProcess(className, null);
            Condition newProcess = new Condition(coreProcess);

            //AbstractBooleanCondition newCondition = _coreFactory.CreateCondition(className);
            //AbstractBooleanCondition newCondition = GAFactory.Instance.CreateCondition(className);

            //newCondition.Attach(LogObserver.Instance);
            _processes.Add(className, newProcess);  // we'll need it for links
            _schema.AddProcess(newProcess); // we need it for BlackBoard access
        }

        private void ProcessSimpleLink(int idFrom, int idTo)
        {

            Process fromProcess = (Process)_processes[idFrom];
            Process toProcess = (Process)_processes[idTo];
            AbstractLink newLink = new SimpleLink(toProcess);
            fromProcess.AddLink(newLink);
        }

        private void ProcessConditionalLink(int idFrom, int idTo,
                                            string conditionName,
                                            bool greenLightResult)
        {
            Process fromProcess = (Process)_processes[idFrom];
            Process toProcess = (Process)_processes[idTo];
            Condition condition = (Condition)_processes[conditionName];

            AbstractLink newLink = new ConditionalLink(toProcess, condition, greenLightResult);
            fromProcess.AddLink(newLink);
        }

        
        private static string GetStringAttribute(XmlNode node, string attrName)
        {
            string result;

            if (node.Attributes[attrName] == null)
            {
                result = "";
            }
            else
            {
                result = node.Attributes[attrName].Value;
            }

            return result;
        }

        private static int GetIntAttribute(XmlNode node, string attrName)
        {
            return Convert.ToInt32(GetStringAttribute(node, attrName));
        }

        private static bool GetBoolAttribute(XmlNode node, string attrName)
        {
            return Convert.ToBoolean(GetStringAttribute(node, attrName));
        }

        private static bool hasConstructorParameters(XmlNode node)
        {
            return (node.HasChildNodes && (node.ChildNodes[0].Name == "parameter"));
        }

        private object[] GetProcessParameters(XmlNode node)
        {
            object[] result = new object[node.ChildNodes.Count];
            int i = 0;

            foreach (XmlNode subNode in node.ChildNodes)
            {
                result[i] = GetStringAttribute(subNode, "paramValue");
                i++;
            }

            return result;
        }

        // ToDo by Lum 06-06-2007: Delete this dependency between GA & Workflow
        private void LoadCityList(XmlNode node)
        {
            string fileName = GetStringAttribute(node, "fileName");
            if (!File.Exists(fileName))
            {
                if ('/' == fileName[0])
                    fileName = Configuration.ProjectBaseDir + fileName;
                else
                    fileName = Configuration.ProjectBaseDir + "/" + fileName;
            }
            if (File.Exists(fileName)){
                CityList.Instance.LoadFromFile(fileName);
            }
            else {
                throw new FileNotFoundException();
            }
        }


        private void LoadProcess(XmlNode node)
        {
            string className = GetStringAttribute(node, "className");
            string terminationFlag = GetStringAttribute(node, "terminationFlag");
            int id = GetIntAttribute(node, "id");
            object[] constructorParameters = null;

            if (hasConstructorParameters(node))
            {
                constructorParameters = GetProcessParameters(node);
            }
            //if (isContainer) Parse(subNode, indent + "  ");  

            ProcessNode(id, className, terminationFlag, constructorParameters);
        }

        private bool IsConditionalLink(XmlNode node)
        {
            return (GetStringAttribute(node, "conditionName") != "");
        }

        private void LoadLink(XmlNode node)
        {
            int from = GetIntAttribute(node, "idFrom");
            int to = GetIntAttribute(node, "idTo");

            if (IsConditionalLink(node))
            {
                string conditionName = GetStringAttribute(node, "conditionName");
                bool greenLightResult = GetBoolAttribute(node, "result");

                ProcessConditionalLink(from, to, conditionName, greenLightResult);
            }
            else
            {
                ProcessSimpleLink(from, to);
            }
        }

        private void LoadCondition(XmlNode node)
        {
            string condName = GetStringAttribute(node, "className");
            ProcessCondition(condName);
        }

        private void Parse(XmlNode node)
        {
            foreach (XmlNode subNode in node.ChildNodes)
            {
                switch (subNode.Name)
                {
                    case "cityList":
                        LoadCityList(subNode);
                        break;
                    case "process":
                        LoadProcess(subNode);
                        break;
                    case "condition":
                        LoadCondition(subNode);
                        break;
                    case "link":
                        LoadLink(subNode);
                        break;
                }
            }
        }

	}
}
